/*
 *  $Id: CardDeck.cs 1218 2010-11-28 14:03:06Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008-2010 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections;
using System.Collections.Generic;

using Nereal.Serialization;

namespace Ragima {
    /// <summary>
    /// Колода карт.
    /// </summary>
    [XmlRootName("deck")]
    public sealed class CardDeck : IEnumerable<DeckItem> {
        private string _name;
        private List<DeckItem> _deck, _reserve;

        public CardDeck(): this("noname") {}
        public CardDeck(string name) {
            _name = name;
            _deck = new List<DeckItem>();
            _reserve = new List<DeckItem>();
        }

        /// <summary>
        /// Загружает колоду из указанного файла.
        /// </summary>
        public static CardDeck LoadFrom(string filename) {
            var path = Paths.Decks[filename];
            return path.Exists ? Serializer.Default.Deserialize<CardDeck>(path) : null;
        }

        /// <summary>
        /// Создает колоду с определенными названием и условием.
        /// </summary>
        /// <param name="name">Название колоды</param>
        /// <param name="predicate">Условия выбора карт</param>
        /// <returns>Созданная колода</returns>
        public static CardDeck CreateDeck(string name, Predicate<Card> predicate) {
            var newDeck = new CardDeck(name);
            foreach (var card in CardManager.GetCards())
                if ((predicate == null || predicate(card)) && card.MaxCount > 0)
                    newDeck.Add(DeckItem.CreateMax(card), false);
            return newDeck;
        }
        
        /// <summary>
        /// Создает полную колоду из всех возможных карт.
        /// </summary>
        /// <returns>Созданная колода</returns>
        public static CardDeck CreateFullDeck() {
            return CreateDeck(Resources.Deck.FullName.GetText(), null);
        }
        /// <summary>
        /// Создает колоду из всех карт определенной стихии.
        /// </summary>
        /// <param name="element">Стихия</param>
        /// <returns>Созданная колода</returns>
        public static CardDeck CreateElementDeck(ElementType element) {
            return CreateDeck(Resources.Deck.ElementName.GetText(element), card => card.Element == element || (card.CardType == CardType.PlayerAbility && card.Element == ElementType.Neutral));
        }

        /// <summary>
        /// Сохраняет колоду в указанный файл.
        /// </summary>
        /// <param name="filename">
        /// A <see cref="System.String"/>
        /// </param>
        public void Save(string filename) {
            var path = Paths.Decks[filename];
            Serializer.Default.Serialize(this, path);
        }

        /// <summary>
        /// Копирует колоду и возвращает готовую копию.
        /// </summary>
        public CardDeck Copy() {
            var newDeck = new CardDeck(Name);
            foreach (var item in _deck)
                newDeck._deck.Add(item.Copy());
            foreach (var item in _reserve)
                newDeck._reserve.Add(item.Copy());
            return newDeck;
        }

        /// <summary>
        /// Название колоды.
        /// </summary>
        [XmlName("name")]
        public string Name {
            get { return _name; }
            set { _name = value; }
        }

        [XmlName("cards", "card")]
        public List<DeckItem> Cards {
            get { return _deck; }
        }

        [XmlName("reserve", "card")]
        public List<DeckItem> ReserveCards {
            get { return _reserve; }
        }

        public int Count {
            get { return _deck.Count; }
        }
        public int ReserveCount {
            get { return _reserve.Count; }
        }

        public int GetCount(bool reserve) {
            return GetList(reserve).Count;
        }
        public static int GetMaxCount(bool reserve) {
            return reserve ? Constants.MaxDeckReserveSize : Constants.MaxDeckSize;
        }
        public bool IsFull(bool reserve) {
            return GetCount(reserve) >= GetMaxCount(reserve);
        }

        public DeckItem this[int index] {
            get { return _deck[index]; }
        }

        public DeckItem GetReserveCard(int index) {
            return _reserve[index];
        }

        public void Clear() {
            _deck.Clear();
            _reserve.Clear();
        }

        private List<DeckItem> GetList(bool reserve) {
            return reserve ? _reserve : _deck;
        }

        public DeckItem Find(Card card, bool reserve) {
            foreach (var item in GetList(reserve))
                if (item.Card == card)
                    return item;
            return null;
        }

        public void Add(DeckItem item, bool reserve) {
            var existItem = Find(item.Card, reserve);
            if (existItem != null) {
                existItem.Count += item.Count;
            } else {
                GetList(reserve).Add(item);
            }
        }
        public void Add(Card card, bool reserve) {
            var existItem = Find(card, reserve);
            if (existItem != null) {
                existItem.Count += 1;
            } else {
                GetList(reserve).Add(new DeckItem(card));
            }
        }
        public void Remove(DeckItem item, bool reserve) {
            GetList(reserve).Remove(item);
        }
        public void Remove(Card card, bool reserve) {
            var existItem = Find(card, reserve);
            if (existItem != null) {
                existItem.Count -= 1;
                if (existItem.Count <= 0)
                    Remove(existItem, reserve);
            }
        }

        public int GetCountOf(Card card) {
            int count = 0;
            var item = Find(card, false);
            if (item != null)
                count += item.Count;
            item = Find(card, true);
            if (item != null)
                count += item.Count;
            return count;
        }

        public int SummaryValue {
            get {
                int sum = 0;
                foreach (var item in _deck)
                    sum += item.Value;
                return sum;
            }
        }

        public ElementValues ElementSummaryValue {
            get {
                var values = new ElementValues();
                foreach (var item in _deck)
                    values.Increment(item.Card.Element, item.Value);
                return values;
            }
        }

        public ElementType MainElement {
            get {
                var values = ElementSummaryValue;
                var main = ElementType.Neutral;
                foreach (var element in ElementType.AllElements)
                    if (values[element] > values[main])
                        main = element;
                return main;
            }
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return _deck.GetEnumerator();
        }
        IEnumerator<DeckItem> IEnumerable<DeckItem>.GetEnumerator() {
            return _deck.GetEnumerator();
        }
    }

    /// <summary>
    /// Элемент колоды карт, представляющий собой одну карту.
    /// </summary>
    [XmlRootName("card")]
    public sealed class DeckItem {
        [XmlName("id"), Reference(true, typeof(string), Serialization.Resolvers.CardId)]
        public Card Card;
        [XmlName("count"), DefaultValue(1)]
        public int Count;

        public DeckItem() : this(null, 0) {
        }
        public DeckItem(Card card) : this(card, 1) {
        }
        public DeckItem(Card card, int count) {
            Card = card;
            Count = count;
        }

        public int Value {
            get { return Card.Value * Count; }
        }

        public DeckItem Copy() {
            return new DeckItem(Card, Count);
        }

        public static DeckItem CreateMax(Card card) {
            return new DeckItem(card, card.MaxCount);
        }
    }

    public class HandCard {
        public bool Opened;
        public Card Card;

        public HandCard() {}
        public HandCard(bool opened, Card card) {
            Opened = opened;
            Card = card;
        }
    }
}
